package com.ruoyi.jzzgl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.jzzgl.domain.entity.JzzglSkjl;
import com.ruoyi.jzzgl.domain.bo.JzzglSkjlBo;
import com.ruoyi.jzzgl.domain.entity.JzzglZdgl;
import com.ruoyi.jzzgl.domain.vo.JzzglSkjlVo;
import com.ruoyi.jzzgl.service.JzzglSkjlService;
import com.ruoyi.jzzgl.mapper.JzzglSkjlMapper;
import com.ruoyi.jzzgl.service.JzzglZdglService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.jzzgl.domain.vo.StatisticsVo;

import java.util.Collections;

import javax.annotation.Resource;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Date;
import java.util.HashMap;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateField;

/**
 * 刷卡记录Service业务层处理
 *
 * @author xq
 */
@RequiredArgsConstructor
@Service
public class JzzglSkjlServiceImpl extends ServiceImpl<JzzglSkjlMapper, JzzglSkjl>
    implements JzzglSkjlService {

    @Resource
    JzzglSkjlMapper jzzglSkjlMapper;
    @Resource
    JzzglZdglService jzzglZdglService;

    /**
     * 分页查询刷卡记录列表
     */
    @Override
    public TableDataInfo<JzzglSkjlVo> queryPageList(JzzglSkjlBo bo, PageQuery pageQuery) {
        Page<JzzglSkjlVo> page = pageQuery.build();
        IPage<JzzglSkjlVo> result = jzzglSkjlMapper.selectSkjlPageList(page, bo);
        return TableDataInfo.build(result);
    }

    /**
     * 批量新增刷卡记录
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchInsert(List<JzzglSkjlBo> boList) {
        List<JzzglSkjl> list = new ArrayList<>();
        for (JzzglSkjlBo bo : boList) {
            JzzglSkjl jzzglSkjl = new JzzglSkjl();
            BeanUtil.copyProperties(bo, jzzglSkjl, "id");
            jzzglSkjl.setCreateTime(DateUtil.date(bo.getCreateTime()));
            list.add(jzzglSkjl);
        }
        saveBatch(list);
    }

    @Override
    public JzzglSkjlVo queryById(Long id) {
        JzzglSkjl jzzglSkjl = this.getById(id);
        if (jzzglSkjl != null) {
            JzzglSkjlVo jzzglSkjlVo = new JzzglSkjlVo();
            BeanUtil.copyProperties(jzzglSkjl, jzzglSkjlVo);
            JzzglZdgl zdgl = jzzglZdglService.getById(jzzglSkjl.getStationId());
            if (zdgl != null) {
                jzzglSkjlVo.setStationName(zdgl.getStationName());
            }
            return jzzglSkjlVo;
        }
        return null;
    }

    @Override
    public Integer getTodayRecords(Integer stationId) {
        LambdaQueryWrapper<JzzglSkjl> lqw = Wrappers.lambdaQuery();
        lqw.ge(JzzglSkjl::getCreateTime, DateUtil.beginOfDay(new Date()))
            .le(JzzglSkjl::getCreateTime, DateUtil.endOfDay(new Date()));
        if (stationId != null) {
            lqw.eq(JzzglSkjl::getStationId, stationId);
        }
        return Math.toIntExact(this.count(lqw));
    }

    @Override
    public Integer getTodayElderlyRecords(Integer stationId) {
        LambdaQueryWrapper<JzzglSkjl> lqw = Wrappers.lambdaQuery();
        // 今日范围
        lqw.ge(JzzglSkjl::getCreateTime, DateUtil.beginOfDay(new Date()))
            .le(JzzglSkjl::getCreateTime, DateUtil.endOfDay(new Date()))
            // 65岁以上
            .le(JzzglSkjl::getBirthday, DateUtil.offset(new Date(), DateField.YEAR, -65));

        if (stationId != null) {
            lqw.eq(JzzglSkjl::getStationId, stationId);
        }
        return Math.toIntExact(this.count(lqw));
    }

    @Override
    public Double getElderlyRatio(Integer stationId) {
        LambdaQueryWrapper<JzzglSkjl> totalLqw = Wrappers.lambdaQuery();
        if (stationId != null) {
            totalLqw.eq(JzzglSkjl::getStationId, stationId);
        }
        long total = this.count(totalLqw);
        if (total == 0) {
            return 0.0;
        }

        LambdaQueryWrapper<JzzglSkjl> elderlyLqw = Wrappers.lambdaQuery();
        Date date65YearsAgo = DateUtil.offset(new Date(), DateField.YEAR, -65);
        elderlyLqw.le(JzzglSkjl::getBirthday, date65YearsAgo);
        if (stationId != null) {
            elderlyLqw.eq(JzzglSkjl::getStationId, stationId);
        }
        long elderlyCount = this.count(elderlyLqw);

        BigDecimal ratio = new BigDecimal(elderlyCount * 100.0 / total)
            .setScale(2, RoundingMode.HALF_UP);
        return ratio.doubleValue();
    }

    @Override
    public StatisticsVo.TrendData getTrendData(Integer stationId, Date startDate, Date endDate) {
        StatisticsVo.TrendData trendData = new StatisticsVo.TrendData();
        List<String> dates = new ArrayList<>();
        List<Integer> totalCounts = new ArrayList<>();
        List<Integer> elderlyCounts = new ArrayList<>();

        // 一次性查询所有数据
        // 计算老年人判断日期
        Date elderlyDate = DateUtil.offset(new Date(), DateField.YEAR, -65);

        List<Map<String, Object>> dbData = jzzglSkjlMapper.selectTrendData(
            stationId,
            elderlyDate,
            startDate != null ? DateUtil.beginOfDay(startDate) : null,
            endDate != null ? DateUtil.endOfDay(endDate) : null
        );

        // 新增逻辑：当入参日期为空时，使用数据库查询结果中的日期范围
        if (startDate == null && endDate == null && !dbData.isEmpty()) {
            List<Date> datesInDb = dbData.stream()
                .map(m -> (Date) m.get("date"))
                .sorted()
                .collect(Collectors.toList());
            startDate = DateUtil.beginOfDay(datesInDb.get(0));
            endDate = DateUtil.endOfDay(datesInDb.get(datesInDb.size() - 1));
        }

        // 生成日期序列（使用调整后的日期范围）
        List<DateTime> dateTimes = DateUtil.rangeToList(
            startDate != null ? startDate : new Date(),
            endDate != null ? endDate : new Date(),
            DateField.DAY_OF_YEAR
        );

        // 转换为日期为key的Map
        Map<String, Map<String, Long>> dataMap = dbData.stream().collect(
            Collectors.groupingBy(
                m -> DateUtil.format((Date) m.get("date"), "yyyy-MM-dd"),
                Collectors.collectingAndThen(
                    Collectors.toList(),
                    list -> {
                        Map<String, Long> counts = new HashMap<>();
                        counts.put("total", list.stream().mapToLong(m -> ((Number) m.getOrDefault("total", 0L)).longValue()).sum());
                        counts.put("elderly", list.stream().mapToLong(m -> ((Number) m.getOrDefault("elderly", 0L)).longValue()).sum());
                        return counts;
                    }
                )
            )
        );

        // 填充结果
        for (DateTime date : dateTimes) {
            String dateStr = DateUtil.format(date, "yyyy-MM-dd");
            dates.add(dateStr);

            Map<String, Long> counts = dataMap.get(dateStr);
            totalCounts.add(counts != null ? counts.get("total").intValue() : 0);
            elderlyCounts.add(counts != null ? counts.get("elderly").intValue() : 0);
        }

        trendData.setDates(dates);
        trendData.setTotalCounts(totalCounts);
        trendData.setElderlyCounts(elderlyCounts);
        return trendData;
    }

    @Override
    public List<StatisticsVo.RankData> getRankData(Integer stationId) {
        Date elderlyDate = DateUtil.offset(new Date(), DateField.YEAR, -65);
        List<Map<String, Object>> rankList = stationId == null ?
            baseMapper.selectStationRankWithElderly(5, elderlyDate, null, null) :
            baseMapper.selectStationRankWithElderlyById(stationId, elderlyDate, null, null);

        return rankList.stream().map(map -> {
            StatisticsVo.RankData rankData = new StatisticsVo.RankData();
            rankData.setStationName((String) map.get("stationName"));
            rankData.setCount(((Number) map.get("count")).intValue());
            return rankData;
        }).collect(Collectors.toList());
    }

    @Override
    public List<StatisticsVo.DistributionData> getAgeDistribution(Integer stationId) {
        Date now = new Date();
        List<StatisticsVo.DistributionData> distribution = new ArrayList<>();

        Map<String, Date> ranges = new HashMap<>();
        ranges.put("65岁以上", DateUtil.offset(now, DateField.YEAR, -65));
        ranges.put("45-65岁", DateUtil.offset(now, DateField.YEAR, -45));
        ranges.put("25-45岁", DateUtil.offset(now, DateField.YEAR, -25));

        LambdaQueryWrapper<JzzglSkjl> totalLqw = Wrappers.lambdaQuery();
        if (stationId != null) {
            totalLqw.eq(JzzglSkjl::getStationId, stationId);
        }
        long total = this.count(totalLqw);
        if (total == 0) {
            return distribution;
        }

        Date previousDate = null;
        for (Map.Entry<String, Date> entry : ranges.entrySet()) {
            LambdaQueryWrapper<JzzglSkjl> lqw = Wrappers.lambdaQuery();
            if (previousDate != null) {
                lqw.gt(JzzglSkjl::getBirthday, previousDate);
            }
            lqw.le(JzzglSkjl::getBirthday, entry.getValue());
            if (stationId != null) {
                lqw.eq(JzzglSkjl::getStationId, stationId);
            }
            long count = this.count(lqw);

            StatisticsVo.DistributionData data = new StatisticsVo.DistributionData();
            data.setName(entry.getKey());
            data.setValue(Math.toIntExact((count * 100) / total));
            distribution.add(data);

            previousDate = entry.getValue();
        }

        // 添加25岁以下
        LambdaQueryWrapper<JzzglSkjl> lqw = Wrappers.lambdaQuery();
        lqw.gt(JzzglSkjl::getBirthday, DateUtil.offset(now, DateField.YEAR, -25));
        if (stationId != null) {
            lqw.eq(JzzglSkjl::getStationId, stationId);
        }
        long count = this.count(lqw);
        StatisticsVo.DistributionData data = new StatisticsVo.DistributionData();
        data.setName("25岁以下");
        data.setValue(Math.toIntExact((count * 100) / total));
        distribution.add(data);

        return distribution;
    }

    @Override
    public List<StatisticsVo.DistributionData> getGenderRatio(Integer stationId) {
        LambdaQueryWrapper<JzzglSkjl> totalLqw = Wrappers.lambdaQuery();
        if (stationId != null) {
            totalLqw.eq(JzzglSkjl::getStationId, stationId);
        }
        long total = this.count(totalLqw);
        if (total == 0) {
            return Collections.emptyList();
        }

        List<StatisticsVo.DistributionData> distribution = new ArrayList<>();

        // 统计男性比例
        LambdaQueryWrapper<JzzglSkjl> maleLqw = Wrappers.lambdaQuery();
        maleLqw.eq(JzzglSkjl::getGender, "男");
        if (stationId != null) {
            maleLqw.eq(JzzglSkjl::getStationId, stationId);
        }
        long maleCount = this.count(maleLqw);

        StatisticsVo.DistributionData maleData = new StatisticsVo.DistributionData();
        maleData.setName("男");
        maleData.setValue(Math.toIntExact((maleCount * 100) / total));
        distribution.add(maleData);

        // 统计女性比例
        StatisticsVo.DistributionData femaleData = new StatisticsVo.DistributionData();
        femaleData.setName("女");
        femaleData.setValue(100 - maleData.getValue());
        distribution.add(femaleData);

        return distribution;
    }

    @Override
    public Integer getTotalElderlyRecords(Integer stationId) {
        LambdaQueryWrapper<JzzglSkjl> lqw = Wrappers.lambdaQuery();
        lqw.le(JzzglSkjl::getBirthday, DateUtil.offset(new Date(), DateField.YEAR, -65));
        if (stationId != null) {
            lqw.eq(JzzglSkjl::getStationId, stationId);
        }
        return Math.toIntExact(this.count(lqw));
    }
}




